﻿namespace MailChimp

open System
open System.Collections.Generic
open MailChimp.Types

//==================================================================================
//  ExportApi
//==================================================================================

[<Sealed>]
type ExportApi(apiKey: string, secure: bool) =
    inherit ApiBase(apiKey, secure)

    override x.UserAgent = "EXPORTAPI.NET/1.0"
    override x.ApiUrl = "http://{dc}.api.mailchimp.com/export/1.0/{method}"
    override x.OutputFormat = Format.json
    override x.KeyParam = "apikey"


module ExportHelper =

    let deserialize<'T> x =
        try deserializeJson<'T> x
        with | ex -> deserializeFail ex x

    let deserializeReader<'V,'T> f x: 'V * 'T =
        try let rd = createJsonReader x
            (f rd), deserializeJson<'T> rd
        with | ex -> deserializeFail ex x

    let checkCallError x =
        let e = try deserializeJson<MCError> x |> Some with | _ -> None
        match e with
        | None -> x
        | Some e -> raise (new MCException(e, null, x))


open ExportHelper

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module ExportApi =

    open MailChimp.Types.Export

    let callServer (x: ExportApi) name p f =
        use resp = x.GetResponse(WebMethod.Get, name, None, p)
        use s = resp.GetResponseStream()
        match resp.StatusCode with
        | httpst.OK -> f (new LineReader(s))
        | c -> callServerFail null (int c) (readStream s)

    //------------------------------------------------------------------------------
    //  (*) List
    //------------------------------------------------------------------------------

    let list (x: ExportApi) (listID: string) (status: List.MemberStatus opt)
        (segmentOpts: Campaign.SegmentOptions opt) (since: DateTime opt) =
        let p = Input()
        p.["id"] <- listID
        p.["status"] <- status
        p.["segment"] <- segmentOpts
        p.["since"] <- since
        callServer x "list" (Some p) (fun lr ->
          { Header = lr.ReadLine() |> checkCallError |> deserialize<string[]>
            Data = [| while not (lr.EndOfStream) do yield lr.ReadLine() |> deserialize<obj[]> |] })

    //------------------------------------------------------------------------------
    //  (*) Campaign Subscriber Activity
    //------------------------------------------------------------------------------

    let campaignSubscriberActivity (x: ExportApi) (campaignID: string)
        (includeEmpty: bool opt) (since: DateTime opt) =
        let p = Input()
        p.["id"] <- campaignID
        p.["include_empty"] <- includeEmpty
        p.["since"] <- since
        callServer x "campaignSubscriberActivity" (Some p) (fun lr ->
            let d = Dictionary<string, SubsriberAction[]>()
            seq { while not (lr.EndOfStream) do
                    yield
                        (let l = lr.ReadLine() in if lr.LineNum = 1 then checkCallError l else l) |>
                        deserializeReader<string, SubsriberAction[]> (fun rd ->
                            match rd.MoveToContent() with
                            | Xml.XmlNodeType.Element -> ()
                            | _ -> failwith "Invalid input string."
                            rd.ReadStartElement("root")
                            match rd.GetAttribute("item") @? rd.LocalName with
                            | "item" -> failwith "Invalid input string."
                            | k -> k)
            } |> Seq.iter(fun (k, v) -> d.Add(k, v)); d)

    //------------------------------------------------------------------------------
    //  (*) Ecommerce Orders
    //------------------------------------------------------------------------------

    let ecommOrders (x: ExportApi) (since: DateTime opt) =
        let p = Input()
        p.["since"] <- since
        callServer x "ecommOrders" (Some p) (fun lr ->
            [| while not (lr.EndOfStream) do
                 yield
                    (let l = lr.ReadLine() in if lr.LineNum = 1 then checkCallError l else l) |>
                    deserialize<Export.EcommOrder> |])

//==================================================================================
//  ExportApi augmentation
//==================================================================================

module EA = ExportApi

type ExportApi with

    //------------------------------------------------------------------------------
    //  (*) List
    //------------------------------------------------------------------------------

    member x.List(listID, [<op>]status, [<op>]segmentOpts, [<op>]since) =
        EA.list x listID status segmentOpts since
    member x.List(listID, ?status, ?segmentOpts, ?since) =
        EA.list x listID ((<?)status) ((<?)segmentOpts) ((<?)since)

    //------------------------------------------------------------------------------
    //  (*) Campaign Subscriber Activity
    //------------------------------------------------------------------------------

    member x.CampaignSubscriberActivity(campaignID, [<op>]includeEmpty, [<op>]since) =
        EA.campaignSubscriberActivity x campaignID includeEmpty since
    member x.CampaignSubscriberActivity(campaignID, ?includeEmpty, ?since) =
        EA.campaignSubscriberActivity x campaignID ((<?)includeEmpty) ((<?)since)

    //------------------------------------------------------------------------------
    //  (*) Ecommerce Orders
    //------------------------------------------------------------------------------

    member x.EcommOrders([<op>]since) = EA.ecommOrders x since
    member x.EcommOrders(?since) = EA.ecommOrders x ((<?)since)